iT邦幫忙

2024 iThome 鐵人賽

DAY 6
0
生成式 AI

Python 新手的 AI 之旅:從零開始打造屬於你的 AI / LLM 應用系列 第 6

【Day6】讓模型使用工具 (1):連 Strawberry 有幾個 r 都不會算?那就用程式算吧!

  • 分享至 

  • xImage
  •  

連這個都不會?

最近有一篇很紅的貼文,內容是原 po 問 GPT-4o:「Strawberry 有幾個 r」,你會發現這麼厲害的模型竟然會敗在這種小問題!這讓大家開始對 “AI” 產生質疑。

image.png

(圖片來源:X)

當然,如果你有一些基本概念的話就會知道這個是很正常的,但要解決語言接龍無法辦到的事,我們可以用外部工具來解決!

讓模型使用工具的原理

在進入正題之前,我們來看看一篇叫 toolformer 的論文。它的概念在於讓模型去自主學習如何 call API,以達到使用多種外部工具的效果,這些外部工具包含計算機、維基百科等等。以下圖片有顏色的部分就是模型自己決定要 call API 的地方,是不是很聰明呀!論文中關於學習這件事,採用了微調 (fine-tune) 這個方法,之後會再跟大家介紹一番,你只要知道它是直接改變語言模型的輸出,而不是用「詠唱 (prompt)」的方式來達到效果。

image.png

你可能會想說他如果要做到「自主學習」這件事,訓練用的資料要怎麼生成,這些特定的 token 不用人去幫忙標註嗎?誒,還真的不用,它用了一個 prompt engineering 的技巧叫 “In context learning (few-shot learning)”,其核心在於提供範例給 LLM,讓他根據範例「學習」如何輸出正確的字串。下圖是論文提供的例子,這邊的 context 是跟魔行說可以在需要的時候,用 [QA(question)] 來呼叫 API,並且跟他說 question 是你想問的問題。

image.png

這種讓語言模型使用工具的論文還有 WebGPT,它也是用微調 GPT-3 的方式來讓模型輸出特定的字串,讓程式碼來呼叫外部 API,有興趣的朋友可以自行閱讀。

開始實作

設計 Prompt

雖然我們還沒有要微調任何模型,但我們可以用這種提供範例給模型的方法,來讓模型判斷要 call API 的時機,然後再用程式碼去檢查字串,當看到特定 API 字串就使用 function 來填入空缺。

以下是我讓模型判斷如果要用 API call 來計算字數,就用 <API>(input_string, target_character)</API> 這個東東來替換掉結果。這種用法叫 XML 標籤,是 Prompt Engineering 中很常出現,用來標記東東用的。

這是我的 prompt

你的任務是幫助使用者計算字母的數量。
如果使用者要計算字母的數量,用 <API>(input_string, target_character)</API> 來替換掉計算的結果。
並且加上一些自然語言的回覆。
你可以把 "input_string" 改成要計算的字串;把 "target_character" 改成目標字母。
並且使用**繁體中文**回答問題。

感覺結果是還不錯啦…但就是他還會「搞威」把 當成他的方法在說明,但我要的其實是直接將他要回答的數字改成這個 才對。

image.png

先前的 prompt 廢話太多,像「如果使用者要計算字母的數量」本身就容易造成混淆,我們刪掉這句話然後小調整一下。這是修改後的 prompt:

你的任務是幫助使用者計算字母的數量。
在你要表達數字的地方用 <API>(input_string, target_character)</API> 來表達。
你可以把 "input_string" 改成要計算的字串;把 "target_character" 改成目標字母。
並且使用**繁體中文**回答問題。

執行結果如下,感覺好多了!不過還是有個小問題,注意到第三次的回覆,模型很雞婆的加上了引號,但我們希望每次的結構都是固定的才對

image.png

再調整一下 prompt,告訴模型不要有引號出現,並且再簡化,連括號也不要了。而且我們嚴格的給他更多限制。

你的任務是幫助使用者計算字母的數量。
在你要表達數字的地方用 <API>input_string, target_character</API> 來表達。
把 input_string 改成要計算的字串,target_character 改成目標字母。
注意:
1. 不要用引號來包住 input_string 和 target_character。
2. 不要在 input_string 和 target_character 之間加任何符號,例如逗號、空格或其他符號。
3. 不用額外說明計算過程,可以加上自然語言潤飾句子
4. 使用**繁體中文**回答問題。

image.png

總算是我們要的結果。

完整程式碼

這邊我們要來實作 API 的功能了,calculate_letter_count 用來計算字元數量,format_string 會判斷有沒有 <API> token,如果有就填入使用 function 計算的新句子,沒有就回傳原本的句子

其他的程式碼都和前幾天的一樣,只有 prompt 不同和多了兩個函式。

# 使用 openai 的 API 來生成文本
import openai
from dotenv import load_dotenv # 載入 dotenv 套件
import os

load_dotenv() # 載入環境變數

model_name = "gpt-4o-mini"

# 從環境變數中取得 API 金鑰,並且設定給 openai
openai.api_key = os.getenv("OPENAI_API_KEY") 

client = openai.OpenAI() # 建立 OpenAI 客戶端

def calculate_letter_count(input_string, target_character):
    # 計算 input_string 中 target_character 的數量
    return input_string.count(target_character)

def format_string(ai_response_string):
    start_index = ai_response_string.find("<API>") # 找到 <API> 的開始位置,如果找不到會回傳 -1
    if start_index != -1:
        end_index = ai_response_string.find("</API>", start_index) # 找到 </API> 的結束位置
        api_content = ai_response_string[start_index+5:end_index] # 取得 API 內容
        input_string, target_character = api_content.split(',') # 分割 input_string 和 target_character
        letter_count = calculate_letter_count(input_string.strip(), target_character.strip()) # 計算字母數量
        return ai_response_string[:start_index] + str(letter_count) + ai_response_string[end_index+6:] # 把計算結果放回 ai_response_string 中
    return ai_response_string

# 提示的 propmt
prompt = '''
你的任務是幫助使用者計算字母的數量。
在你要表達數字的地方用 <API>input_string, target_character</API> 來表達。
把 input_string 改成要計算的字串,target_character 改成目標字母。
注意:
1. 不要用引號來包住 input_string 和 target_character。
2. 不要在 input_string 和 target_character 之間加任何符號,例如逗號、空格或其他符號。
3. 不用額外說明計算過程,可以加上自然語言潤飾句子
4. 使用**繁體中文**回答問題。
'''

# 初始化對話歷史
messages = [
    {
        "role": "system",
        "content": prompt
    }
]

user_input = input("你:")

# 將用戶輸入加到對話歷史
messages.append({"role": "user", "content": user_input})

    
# 發送請求給 OpenAI API
completion = client.chat.completions.create(
    model=model_name,
    messages=messages
    )
    
# 獲取 AI 的回應
ai_response = completion.choices[0].message.content

ai_response = format_string(ai_response)
    
# 印出 AI 的回應
print(f"AI:{ai_response}")

執行結果,可以看到數量真的都是對的 (我實在沒有 get 到他的笑話XD)

image.png

小結

到這邊你應該會發現,開發 LLM 應用其實就已經身在機率的世界了,只能不斷測試和改良,超好玩的~都是憑感覺在通靈XD。其實也不一定要自己設計 prompt,你可以用一些好用的 prompt generate 工具來生成 prompt,像是 anthropic console 就有一個產生 prompt 的功能;我剛剛查到 還有 neural writerThe Free AI Prompt Generator 巴拉巴拉一堆,真慶幸活在這個時代啊

機率這件事對工程來說真的蠻頭痛的,如果 LLM 生成的句子不符合程式碼的格式怎麼辦?我們這種小程式是還好,就 Python 噴錯而已,但如果一個系統的流程因為這樣而崩潰就糟了…

明天會分享使用模型供應商 (OpenAI, Groq) 提供的 tool use 選項,讓模型可以比較穩定的使用函式呼叫,期待一下吧~ (我覺得這幾天都用 gpt-4o-mini 有點不公平,明天來用用 llama 吧 🦙,我最喜歡 llama ㄌ)


上一篇
【Day5】初探 AI 記憶功能:讓你的 LLM 不再「失憶」
下一篇
【Day7】讓模型使用工具 (2):透過 tool use (function calling) API 提升模型輸出穩定性
系列文
Python 新手的 AI 之旅:從零開始打造屬於你的 AI / LLM 應用12
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言